﻿/**
 * Copyright (C) 2013 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AopAlliance.Intercept;
using System.Reflection;

namespace NGuice.Inject.Internal
{
    //compelte!
    /// <summary>
    /// 使用一个拦截器栈拦截一个方法，他使用castle dynamic proxy库的拦截器实现
    /// </summary>
    internal class InterceptorStackCallback : Castle.Core.Interceptor.IInterceptor
    {
        readonly IMethodInterceptor[] interceptors;
        readonly MethodInfo method;

        public InterceptorStackCallback(MethodInfo method, List<IMethodInterceptor> interceptors)
        {
            this.method = method;
            this.interceptors = interceptors.ToArray();
        }

        public void Intercept(Castle.Core.Interceptor.IInvocation invocation)
        {
            new InterceptedMethodInvocation(this, invocation).Proceed();
        }

        private class InterceptedMethodInvocation : IMethodInvocation
        {
            readonly InterceptorStackCallback callback;
            //readonly object proxy;
            //readonly object[] arguments;
            readonly Castle.Core.Interceptor.IInvocation methodProxy;
            int index = -1;

            public InterceptedMethodInvocation(InterceptorStackCallback callback, 
                                               Castle.Core.Interceptor.IInvocation methodProxy)
            {
                this.callback = callback;
                //this.proxy = methodProxy.Proxy;
                this.methodProxy = methodProxy;
                //this.arguments = methodProxy.Arguments;
            }

            public object Proceed()
            {
                try
                {
                    index++;
                    if (index == callback.interceptors.Length)
                    {
                        methodProxy.Proceed();
                        return methodProxy.ReturnValue;
                    }
                    else
                    {
                        return callback.interceptors[index].Invoke(this);
                    }
                }
                finally
                {
                    index--;
                }
            }

            public MethodInfo Method
            {
                get
                {
                    return callback.method;
                }
            }

            public object[] Arguments
            {
                get
                {
                    return methodProxy.Arguments;
                }
            }

            public object This
            {
                get { return methodProxy.Proxy; }
            }

            public object Proxy
            {
                get { return methodProxy.Proxy; }
            }

            public object Target
            {
                get { return methodProxy.InvocationTarget; }
            }

            public Type TargetType
            {
                get { return methodProxy.TargetType; }
            }

            public MemberInfo StaticPart
            {
                get { return Method; }
            }
        }
    }

     
}
